import json
import random

from flask_restful import Resource
from flask_restful.reqparse import RequestParser

from comment.models import db
from comment.models.user import User  # 这里将模型导入进来以后，再执行migrate.py文件中的数据库相关的命令时才会将模型类映射到数据库中
from flask import current_app, request, g

from comment.utils.decorators import login_required
from comment.utils.token_pyjwt import generate_token, verify_tokens
from comment.utils import parser
from comment.utils.shoppping_redis import redis_client
from comment.utils.sms_code_send import sms_send
from comment.utils.limiter import limiter as lmt
from shopping.resource.user import constants
from flask_limiter.util import get_remote_address


class User_Resource(Resource):
    '''
    给函数加上登录的拦截
    '''
    # 在restful风格中使用装饰器时
    method_decorators = {
        'get': [login_required],
        'post': [login_required]
    }

    def get(self):
        current_app.logger.info('我的测试日志')
        # 这里的代码可能会用到User模型类
        return {'get': 'get测试'}

    def post(self):
        return {'post': 'post测试'}

    def put(self):
        return {'put': 'put测试'}


class User_SMS(Resource):
    '''
            发送验证码的短信
            :return:
            '''
    # decorators是基于CBV的限频方式，名字不能修改，里面传三个参数，
    # 参数一：限流的速率，参数二：key_func,参数三：如果超出限制之后的提示信息
    # 这个限流器应该是根据获取手机号和客户端Ip地址，分别根据这两个东西进行限制对此视图的访问，和redis并没有直接关系。其在运行时产生的数据保存在配置RATELIMIT_STORAGE_URL中
    error_message = 'Too many requests'
    decorators = [lmt.limit(constants.LIMIT_SMS_CODE_BY_MOBILE,  # 注意：短信平台本身对同一手机号每天发送短信的次数有限制，10或者5次
                            key_func=lambda: request.args['phone'],  # key_func接收的是一个函数，这里传入一个匿名函数
                            error_message=error_message),
                  lmt.limit(constants.LIMIT_SMS_CODE_BY_IP,
                            key_func=get_remote_address,  # 获取客户端的远程ip地址
                            error_message=error_message
                            )
                  ]

    def get(self):
        phone = request.args.get('phone').strip()  # strip 是去除空格
        code = random.randint(1000, 9999)
        result = sms_send(phone, code)  # 返回的是json字符串
        # print(type(result),result)#<class 'str'> {"code":2,"msg":"�ύ�ɹ�","smsid":"16930367826688706474"}
        result = json.loads(result)  # 把json变成字典
        result['phone'] = phone

        # 短信验证码发送成功之后，需要把短信验证码存放到redis数据库中，以便于下次验证。验证码的时效为5分钟。
        from comment.utils.shoppping_redis import redis_client
        redis_client.setex('shopping:code:{}'.format(phone), constants.SMS_CODE_EXPIRY, code)  # 参数一：key 参数二：时效 参数三：验证码

        return result


class AuthorizationCodeResource(Resource):
    '''
    提交手机号和验证码并开始验证
    '''

    def post(self):

        # 先进行参数的格式校验
        req = RequestParser()
        req.add_argument('phone', type=parser.mobile, required=True)
        req.add_argument('code', type=parser.regex(r'^\d{4}$'), required=True)

        # 再进行参数值的校验
        args = req.parse_args()  # 得到所有的参数
        phone = args.phone
        code = args.code
        # 从redis数据库中得到之前保存的验证码
        key = 'shopping:code:{}'.format(phone)
        try:
            real_code = redis_client.get(key)
        except ConnectionError as e:
            current_app.logger.error(e)  # error 是日志的级别，为什么是error？因为这里是记录错误的
            return {'message': 'redis db connect error.'}, 400
        # 开始校验
        if not real_code or real_code.decode() != code:
            return {'message': 'Invalide Code.'}, 400

        return {'phone': phone,
                'msg': 'code success.'}  # 这里的msg不能写成message,会和定义的json输出格式冲突，导致定义的格式没生效，具体原因是什么还没研究，不过也不会报错。


class UserRegisterResource(Resource):
    '''
    注册用户名密码邮箱，要注意这里还要接收手机号
    '''

    def post(self):
        # 先进行参数的格式校验
        req = RequestParser()
        req.add_argument('phone', required=True)
        req.add_argument('username', required=True)
        req.add_argument('password', required=True)
        req.add_argument('email', type=parser.email, required=True)
        args = req.parse_args()
        phone = args.phone
        username = args.username
        password = args.password
        email = args.email

        # 判断用户名是否重复,先从mysql数据库中根据当前用户名进行查询
        u = User.query.filter(User.username == username).first()
        if u:
            current_app.logger.info('{} 用户名已经存在，请换一个'.format(username))
            return {'msg': 'The username already exists.'}, 400

        u = User(phone=phone, username=username, pwd=password, email=email, status=0)  # 用户登录状态，0代表正常
        db.session.add(u)
        db.session.commit()
        return {'msg': 'OK'}


class UserLoginResource(Resource):
    '''用户登录'''

    def post(self):
        # 先进行参数的格式校验
        req = RequestParser()
        req.add_argument('username', required=True)
        req.add_argument('password', required=True)
        # 再从数据库中取值进行比对
        args = req.parse_args()
        username = args.username
        password = args.password
        user = User.query.filter(User.username == username).first()
        if user:
            if user.check_password(password):
                # 必须把登陆成功之后的用户ID得到token,并且把token返回给前端
                token = generate_token(user.id)
                current_app.logger.info(verify_tokens(token))
                return {'msg': 'Login Success.', 'token': token, 'username': username, 'code': 200}
        return {'message': '账号或密码错误'}, 400


class IsExistPhoneResource(Resource):
    '''判断手机号是否存在'''

    def post(self):
        phone = request.form.get('phone')

        user = User.query.filter(User.phone == phone).first()
        if user:
            return {'isExist': True, 'message': '此号码已经存在', 'code': 203}

        return {'message': '手机号可以注册', 'code': 200}  # 这里有message,out_put不会对这里进行再次修改，
        # 也就不会添加状态码，所以这里要传一个code,前端需要通过code来判断后端的状态。


class UserLogoutResource(Resource):
    def post(self):
        if g.user_id:
            g.user_id = None

        return {'msg': '退出登录'}
